package com.ruoyi.system.service.impl;

import java.util.*;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;

import javax.annotation.PostConstruct;

import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.system.mapper.UserInfoMapper;
import com.ruoyi.system.domain.UserInfo;
import com.ruoyi.system.service.IUserInfoService;
import org.springframework.transaction.annotation.Transactional;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.system.mapper.SysUserMapper;
import com.ruoyi.system.service.IAssignNumberBeginService;

/**
 * 用户分配号码Service业务层处理
 * 
 * @author xyz
 * @date 2025-05-14
 */
@Service
public class UserInfoServiceImpl implements IUserInfoService 
{
    @Autowired
    private UserInfoMapper userInfoMapper;

    @Autowired
    private SysUserMapper userMapper;

    @Autowired
    private IAssignNumberBeginService assignNumberBeginService;

    private static final long RESERVED_NUMBERS = 60L;
    private static final Set<String> CUSTOM_EMPLOYEE_IDS =
            new HashSet<>(Arrays.asList("1001","1002"));
    
    // 应用级锁，用于防止并发分配号码
    private static final ReentrantLock numberAssignmentLock = new ReentrantLock();
    // 缓存已分配的号码，用于快速检查
    private static final ConcurrentHashMap<String, Long> allocatedNumbers = new ConcurrentHashMap<>();

    /**
     * 查询用户分配号码
     * 
     * @param id 用户分配号码主键
     * @return 用户分配号码
     */
    @Override
    public UserInfo selectUserInfoById(Long id)
    {
        return userInfoMapper.selectUserInfoById(id);
    }

    /**
     * 查询用户分配号码列表
     * 
     * @param userInfo 用户分配号码
     * @return 用户分配号码
     */
    @Override
    public List<UserInfo> selectUserInfoList(UserInfo userInfo)
    {
        return userInfoMapper.selectUserInfoList(userInfo);
    }

    /**
     * 新增用户分配号码
     * 
     * @param userInfo 用户分配号码
     * @return 结果
     */
    @Override
    public int insertUserInfo(UserInfo userInfo)
    {
        return userInfoMapper.insertUserInfo(userInfo);
    }

    /**
     * 修改用户分配号码
     * 
     * @param userInfo 用户分配号码
     * @return 结果
     */
    @Override
    public int updateUserInfo(UserInfo userInfo)
    {
        return userInfoMapper.updateUserInfo(userInfo);
    }

    /**
     * 批量删除用户分配号码
     * 
     * @param ids 需要删除的用户分配号码主键
     * @return 结果
     */
    @Override
    public int deleteUserInfoByIds(Long[] ids)
    {
        return userInfoMapper.deleteUserInfoByIds(ids);
    }

    /**
     * 删除用户分配号码信息
     * 
     * @param id 用户分配号码主键
     * @return 结果
     */
    @Override
    public int deleteUserInfoById(Long id)
    {
        return userInfoMapper.deleteUserInfoById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult assignNumber(SysUser user) {
        // 使用应用级锁确保号码分配的原子性
        numberAssignmentLock.lock();
        try {
            return doAssignNumber(user);
        } finally {
            numberAssignmentLock.unlock();
        }
    }
    
    /**
     * 实际的号码分配逻辑
     */
    private AjaxResult doAssignNumber(SysUser user) {
        // 1. 检查用户是否存在
        SysUser existingUser = userMapper.selectUserById(user.getUserId());
        if (existingUser == null) {
            return AjaxResult.error("用户不存在");
        }

        // 2. 检查是否已经分配过号码
        if (StringUtils.isNotEmpty(existingUser.getNumber())) {
            return AjaxResult.error("该用户已经分配过号码");
        }

        String number = null;
        String errorDetail = null;
        // 3. 只有isCustom为"1"时才分配dd字段，否则走原有逻辑
        if ("1".equals(existingUser.getIsCustom())) {
            // 检查deadlineTime
            if (existingUser.getDeadlineTime() != null && new Date().after(existingUser.getDeadlineTime())) {
                // 清空dd字段
                existingUser.setDd(null);
                userMapper.updateUser(existingUser);
                return AjaxResult.error("自定义号段已到期，已清空dd字段");
            }
            number = existingUser.getDd();
            user.setIsCustom("1");
            if (StringUtils.isEmpty(number)) {
                errorDetail = String.format("isCustom=1, 但dd字段为空 (userId=%s, dd=%s)", existingUser.getUserId(), existingUser.getDd());
            } else {
                // 检查自定义号码是否已被使用
                if (checkNumberExists(number)) {
                    return AjaxResult.error("自定义号码 " + number + " 已被其他用户使用");
                }
            }
        } else {
            // 使用循环重试机制来避免并发冲突
            int maxRetries = 3;
            for (int retry = 0; retry < maxRetries; retry++) {
                number = generateNumberWithLock(existingUser);
                if (StringUtils.isNotEmpty(number)) {
                    break;
                }
                if (retry == maxRetries - 1) {
                    errorDetail = String.format("isCustom!=1, generateNumber未生成号码，重试%d次后失败 (userId=%s, employeeId=%s)", 
                        maxRetries, existingUser.getUserId(), existingUser.getEmployeeId());
                } else {
                    // 短暂等待后重试
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        return AjaxResult.error("号码分配被中断");
                    }
                }
            }
            user.setIsCustom("0");
        }

        if (StringUtils.isEmpty(number)) {
            return AjaxResult.error("号码生成失败: " + (errorDetail != null ? errorDetail : "未知原因"));
        }

        // 4. 最终检查号码是否已被使用（双重检查）
        if (checkNumberExists(number)) {
            return AjaxResult.error("号码 " + number + " 已被其他用户使用，请重试");
        }

        // 5. 更新用户信息
        user.setNumber(number);
        
        // 添加调试日志
        System.out.println("=== assignNumber 调试信息 ===");
        System.out.println("更新前 user level: " + user.getLevel());
        System.out.println("更新前 user employeeId: " + user.getEmployeeId());
        System.out.println("更新前 user ee: " + user.getEe());
        System.out.println("existingUser level: " + existingUser.getLevel());
        System.out.println("existingUser employeeId: " + existingUser.getEmployeeId());
        System.out.println("existingUser ee: " + existingUser.getEe());
        System.out.println("=============================");
        
        // 使用 existingUser 来更新，确保保留原有字段
        existingUser.setNumber(number);
        existingUser.setIsCustom(user.getIsCustom());
        
        int rows = userMapper.updateUser(existingUser);
        if (rows > 0) {
            // 更新缓存
            allocatedNumbers.put(number, existingUser.getUserId());
            return AjaxResult.success("号码分配成功", number);
        }
        return AjaxResult.error("号码分配失败: 数据库更新失败");
    }

    /**
     * 检查号码是否已被使用
     */
    private boolean checkNumberExists(String number) {
        if (StringUtils.isEmpty(number)) {
            return false;
        }
        
        // 首先检查缓存
        if (allocatedNumbers.containsKey(number)) {
            return true;
        }
        
        // 缓存未命中，查询数据库
        SysUser existingUser = userMapper.selectUserByNumber(number);
        if (existingUser != null) {
            // 更新缓存
            allocatedNumbers.put(number, existingUser.getUserId());
            return true;
        }
        
        return false;
    }
    
    /**
     * 清理号码缓存（当用户被删除或号码被重置时调用）
     */
    public void clearNumberCache() {
        allocatedNumbers.clear();
    }
    
    /**
     * 从缓存中移除指定号码
     */
    public void removeNumberFromCache(String number) {
        if (StringUtils.isNotEmpty(number)) {
            allocatedNumbers.remove(number);
        }
    }

    /**
     * 使用数据库锁生成号码，避免并发冲突
     */
    private String generateNumberWithLock(SysUser user) {
        // 1. 如果有 employeeId 且是自定义号段 (1~59)
        if (StringUtils.isNotEmpty(user.getEmployeeId()) && CUSTOM_EMPLOYEE_IDS.contains(user.getEmployeeId())) {
            // 使用数据库锁查询已使用的1-59号段
            List<String> usedNumbers = userMapper.selectNumbersLessThanWithLock(RESERVED_NUMBERS);
            for (long i = 1; i < RESERVED_NUMBERS; i++) {
                String candidateNumber = String.valueOf(i);
                if (!usedNumbers.contains(candidateNumber)) {
                    // 再次检查号码是否可用
                    if (!checkNumberExists(candidateNumber)) {
                        return candidateNumber;
                    }
                }
            }
            return null; // 1-59号已分配完
        }

        // 2. 普通号段 (>=全局numberBegin)
        long begin = assignNumberBeginService.getGlobalNumberBegin();
        Long maxNumber = userMapper.selectMaxNumberAtLeastWithLock(begin);
        if (maxNumber == null) {
            String candidateNumber = String.valueOf(begin);
            // 检查号码是否可用
            if (!checkNumberExists(candidateNumber)) {
                return candidateNumber;
            }
            // 如果起始号码被占用，查找下一个可用号码
            return findNextAvailableNumber(begin);
        }
        
        String candidateNumber = String.valueOf(maxNumber + 1);
        // 检查号码是否可用
        if (!checkNumberExists(candidateNumber)) {
            return candidateNumber;
        }
        // 如果下一个号码被占用，查找下一个可用号码
        return findNextAvailableNumber(maxNumber + 1);
    }

    /**
     * 查找下一个可用号码
     */
    private String findNextAvailableNumber(long startNumber) {
        // 最多查找100个号码，避免无限循环
        for (long i = startNumber; i < startNumber + 100; i++) {
            String candidateNumber = String.valueOf(i);
            if (!checkNumberExists(candidateNumber)) {
                return candidateNumber;
            }
        }
        return null;
    }

    @Override
    public int resetAllUserNumber() {
        // 直接调用userMapper批量更新number字段为空
        int rows = userMapper.resetAllUserNumber();
        // 清理号码缓存
        clearNumberCache();
        return rows;
    }

    @PostConstruct
    public void init() {
        // 在服务启动时初始化缓存，加载所有已分配的号码
        List<SysUser> users = userMapper.selectAllUsersWithNumbers();
        for (SysUser user : users) {
            if (StringUtils.isNotEmpty(user.getNumber())) {
                allocatedNumbers.put(user.getNumber(), user.getUserId());
            }
        }
        System.out.println("缓存已初始化，当前缓存大小: " + allocatedNumbers.size());
    }
}
